package kr.sdworld.common.util.generator;

import java.rmi.RemoteException;
import java.util.LinkedList;

public final class IDGenerator implements IIDGenerator {
    private static int s_IDCacheSize = Integer.getInteger("idGenerator.IDCacheSize",200).intValue();
    private static int s_ServerID = Integer.getInteger("idGenerator.ServerID", 0).intValue();
    @SuppressWarnings("rawtypes")
	private static LinkedList s_llIDs = new LinkedList();
    private static IIDGenerator s_IDGenerator = new IDGenerator();

    @SuppressWarnings("unchecked")
	private static void fetchIDs()
    {
        long[] lIDs = null;
        try
        {
            lIDs = s_IDGenerator.getID(s_IDCacheSize);
        }
        catch(RemoteException re)
        {
            throw new RuntimeException(re.getMessage());
        }
        for (int i = 0; i < s_IDCacheSize; i++)
        {
            s_llIDs.add(new Long(lIDs[i]));
        }
    }

    public static void setIDGenerator(IIDGenerator idgenerator)
    {
        s_IDGenerator = idgenerator;
    }

    public static synchronized long generate()
    {
        if( s_llIDs.size() == 0 )
        {
            fetchIDs();
        }
        return ((Long)s_llIDs.removeFirst()).longValue();
    }

    public static synchronized long[] generate(int nSize)
    {
        if( nSize <= 0 )
        {
            throw new IllegalArgumentException("nSize is less than zero [" + nSize + "]");
        }
        long[] lIDs = new long[nSize];
        for (int i = 0; i < nSize; i++)
        {
            lIDs[i] = generate();
        }
        return lIDs;
    }

    /**
    * Sequence number that is used to generate the ID.
    */
    private int m_nSeq = 0;

    private long m_oldTime = System.currentTimeMillis();

    public IDGenerator() {}

        /**
	    * This method generate the ID number. The source code that generate ID is as
	    * follows. It generate the ID as sequenced order. // 2000.05.01
	    *
	    * old IDGenerator(2) algorithm comment  ....
	    * Following simple algorithm is designed to optimize the B tree family
	    * index operation(minize the disk usage & reduce the tree balancing overhead).
	    * <blockquote><pre>
	    *   public static synchronized long generate()
	    *   {
	    *       return (s_nSeq++ % 90000L) * 100000000000000L + System.currentTimeMillis();
	    *   }
	    * </pre></blockquote>
	    */
	    public synchronized long getID() throws RemoteException
	    {
	        if( m_nSeq == 99999 )
	        {
	            try
	            {
	                // 30 is rough value(rule of thumb).
	                // Less than 30 may cause the problem. Do not believe the Thread.sleep()
	                Thread.sleep(30);
	            }
	            catch(InterruptedException e) {}
	        }
	
	        long lNewTime = System.currentTimeMillis();
	        if(lNewTime == m_oldTime)
	        {
	            ++m_nSeq;
	        }
	        else
	        {
	            m_nSeq = 0;
	        }
	        m_oldTime = lNewTime;
	        long lID = (m_nSeq % 100000L) + lNewTime * 1000000L;
	        
	        // 클러스터링 구성시 중복 방지를 위해 서버 ID를 추가한다. 6 번째 자리가 서버 ID 이다.
	        // 2262년까지만 유효하다.
	        lID += s_ServerID * 100000;
	        return lID;
	//        return (s_nSeq++ % 90000L) * 100000000000000L + System.currentTimeMillis(); // old IDgenerator(2)
	//        return (s_nSeq++ % 100000L) + System.currentTimeMillis() * 100000L; // old IDgenerator(1)
	    }

    public long[] getID(int nSize) throws RemoteException
    {
        if( nSize <= 0 )
        {
            throw new IllegalArgumentException( "IDGenerator's generate() : nSize is " + nSize );
        }

        long[] arrID = new long[nSize];
        for(int i = 0; i < nSize; i++)
        {
            arrID[i] = getID();
        }
        return arrID;
    }
}
